package com.zg.autoform.web;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.zg.autoform.enm.LayoutTypeEnum;
import com.zg.autoform.service.FormService;
import com.zg.autoform.util.TableUtil;
import com.zg.autoform.vo.DataSelectVo;
import com.zg.autoform.web.req.FormListReq;
import com.zg.autoform.web.req.FormTablePageReq;
import com.zg.common.core.A;
import com.zg.common.core.C;
import com.zg.common.core.R;
import com.zg.common.core.dao.BaseEntity;
import com.zg.common.core.req.PageReq;
import com.zg.common.dds.DbTypeUtil;
import com.zg.common.web.Req;
import com.zg.common.core.dao.autoform.*;
import com.zg.common.core.util.JsonUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.annotations.Param;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Zxb
 */
@RestController
@RequiredArgsConstructor
@Api(tags = {"表单管理"})
public class FormController {
    private final DiyFormDao formDao;
    private final DiyColumnDao columnDao;
    private final DiyFormulaDao formulaDao;
    private final DiyLayoutDao layoutDao;
    private final DiyHeaderDao headerDao;
    private final DiyConditionDao conditionDao;
    private final JdbcTemplate jdbcTemplate;
    private final TableUtil tableUtils;
    private final DbTypeUtil dbTypeUtil;

    private final FormService formService;

    @PostMapping("/form/add")
    public R add(@RequestBody DiyForm obj) {
        if (formDao.exists(
                new QueryWrapper<DiyForm>()
                        .lambda()
                        .eq(DiyForm::getName, obj.getName())
        )) {
            return R.error("表单名称已经存在");
        }
        obj.setCode(obj.getCode().toUpperCase());
        if (formDao.exists(
                new QueryWrapper<DiyForm>()
                        .lambda()
                        .eq(DiyForm::getCode, obj.getCode())
        )) {
            return R.error("表单编码已经存在");
        }
        obj.setSearchStr(obj.getName() + obj.getCode());
        formDao.save(obj);
        return R.success();
    }

    @GetMapping("/form/topN")
    public R findTopN(String searchStr, Integer topN, Long formId) {
        LambdaQueryWrapper<DiyForm> lqw = new LambdaQueryWrapper<DiyForm>();
        lqw.like(StrUtil.isNotBlank(searchStr), DiyForm::getSearchStr, "%" + searchStr + "%");
        lqw.ne(formId != null, DiyForm::getId, formId);
        if(dbTypeUtil.isOracle()){
            lqw.apply(" ROWNUM<= " + topN);
        }
        lqw.orderByDesc(DiyForm::getId);
        if(dbTypeUtil.isMysql()){
            lqw.last(" limit " + topN);
        }
        List<DiyForm> formList = formDao.list(lqw);
        if (formId != null) {
            DiyForm form = formDao.getById(formId);
            formList.add(0, form);
        }
        return R.success(formList);
    }

    @PostMapping("/form/update")
    public R update(@RequestBody DiyForm obj) {
        DiyForm old = formDao.getById(obj.getId());
        A.isInDatabase(old, "表单");
        if (formDao.exists(
                new QueryWrapper<DiyForm>()
                        .lambda()
                        .eq(DiyForm::getName, obj.getName())
                        .ne(DiyForm::getId, obj.getId())
        )) {
            return R.error("表单名称已经存在");
        }
        BeanUtil.copyProperties(obj, old, C.IGNORED_PROPERTIES);
        old.setSearchStr(obj.getName() + obj.getCode());
        formDao.updateById(old);
        return R.success();
    }

    @PostMapping("/form/del")
    public R del(@RequestBody List<Long> ids) {
        formDao.removeBatchByIds(ids);
        return R.success();
    }

    private ObjectNode generateColumnOn(DiyLayout t, List<DiyColumn> columnList) {
        ObjectNode on = JsonUtil.on();
        Long columnId = t.getColumnId();
        on.put("id", String.valueOf(columnId));
        on.put("span", t.getSpan());
        on.put("offset", t.getOffset());
        on.put("sort", t.getSort());
        on.put("isExist", false);
        DiyColumn column = columnList.stream()
                .filter(c -> c.getId().equals(columnId))
                .findFirst().orElse(null);
        if (column != null) {
            on.put("isExist", true);
            on.put("name", column.getName());
            on.put("code", column.getCode());
            on.put("type", column.getCompType());
            on.put("extra", column.getExtra());
            on.put("defaultVal", column.getDefaultVal());
            on.put("regex", column.getRegex());
            on.put("errorMsg", column.getErrorMsg());
        }
        return on;
    }

    public Map<String, Object> generateFormInfo(DiyForm obj) {
        //获取公式
        List<DiyFormula> formulaList = formulaDao.list(new QueryWrapper<DiyFormula>()
                .lambda().eq(DiyFormula::getFormId, obj.getId()));
        //获取全部字段
        List<DiyColumn> columnList = columnDao.list(new QueryWrapper<DiyColumn>()
                .lambda().eq(DiyColumn::getFormId, obj.getId()));
        //获取全部字段ID
        List<Long> columnIdList = columnList.stream()
                .map(t -> t.getId())
                .collect(Collectors.toList());
        //获取所有布局
        List<DiyLayout> layoutList = layoutDao.list(new QueryWrapper<DiyLayout>()
                .lambda()
                .in(DiyLayout::getColumnId, columnIdList));
        //获取新增布局字段
        List<ObjectNode> addColumnList = layoutList.stream()
                .filter(x -> obj.getId().equals(x.getFormId()) && LayoutTypeEnum.add.name().equals(x.getType()))
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());
        //获取更新布局字段
        List<ObjectNode> updateColumnList = layoutList.stream()
                .filter(x -> obj.getId().equals(x.getFormId()) && LayoutTypeEnum.update.name().equals(x.getType()))
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());
        //获取批量更新布局字段
        List<ObjectNode> batchColumnList = layoutList.stream()
                .filter(x -> obj.getId().equals(x.getFormId()) && LayoutTypeEnum.batch.name().equals(x.getType()))
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());
        //获取表头设置
        List<DiyHeader> headerList = headerDao.list(new QueryWrapper<DiyHeader>()
                .lambda()
                .eq(DiyHeader::getFormCode, obj.getCode())
                .eq(DiyHeader::getUsername, Req.getCurrUser().getUsername()));
        //是否设置了查询视图
        Boolean hasFilter = conditionDao.exists(new QueryWrapper<DiyCondition>()
                .lambda()
                .eq(DiyCondition::getFormId, obj.getId()));
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", obj.getId());
        map.put("name", obj.getName());
        map.put("code", obj.getCode());
        map.put("addLayout", obj.getAddLayout());
        map.put("updateLayout", obj.getUpdateLayout());
        map.put("batchLayout", obj.getBatchLayout());
        map.put("validUrl", obj.getValidUrl());
        map.put("hasAttach", obj.getHasAttach());
        map.put("attType", obj.getAttType());
        map.put("formulas", formulaList);
        map.put("allColumns", columnList);
        map.put("addColumns", addColumnList);
        map.put("updateColumns", updateColumnList);
        map.put("batchColumns", batchColumnList);
        map.put("headers", headerList);
        map.put("hasFilter", hasFilter);
        return map;
    }

    @GetMapping("/form/oneByCode")
    public R findOneByCode(String code) {
        DiyForm obj = formDao.getOne(new QueryWrapper<DiyForm>()
                .lambda().eq(DiyForm::getCode, code));
        A.isInDatabase(obj, "表单");
        return R.success(generateFormInfo(obj));
    }

    @GetMapping("/form/oneById")
    public R findOneById(Long formId) {
        DiyForm obj = formDao.getById(formId);
        A.isInDatabase(obj, "表单");
        return R.success(generateFormInfo(obj));
    }

    @ApiOperation("查询所有表单列表")
    @PostMapping("/form/list")
    public R<List<DiyForm>> list(@RequestBody FormListReq req) {
        List<DiyForm> list = formDao.list(new LambdaQueryWrapper<DiyForm>().like(DiyForm::getName, req.getName()).eq(BaseEntity::getIsDel, false));
        return R.success(list);
    }

    @GetMapping("/form/page")
    public R page(String name, String code, String searchStr, Integer page, Integer pageSize) {
        IPage<DiyForm> pageParam = new Page<>(page, pageSize);
        QueryWrapper<DiyForm> queryWrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(searchStr)) {
            queryWrapper.lambda().like(DiyForm::getSearchStr, "%" + searchStr + "%");
        }
        if (StrUtil.isNotBlank(name)) {
            queryWrapper.lambda().like(DiyForm::getName, "%" + name + "%");
        }
        if (StrUtil.isNotBlank(code)) {
            queryWrapper.lambda().like(DiyForm::getCode, "%" + code + "%");
        }
        IPage<DiyForm> result = formDao.page(pageParam, queryWrapper);
        return R.success(result);
    }

    @GetMapping("/form/create/table")
    public R page(String id) {
        DiyForm settleForm = formDao.getById(Long.valueOf(id));
        int i = tableUtils.createTable(settleForm.getCode());
        if (i < 0) {
            return R.error(String.valueOf(i));
        }
        return R.success();
    }

    @PostMapping("/form/layout/update")
    public R updateLayout(@RequestBody Map<String, Object> map) {
        Long formId = Long.valueOf((String) map.get("formId"));
        DiyForm old = formDao.getById(formId);
        A.isInDatabase(old, "表单");
        String type = (String) map.get("type");
        String layout = (String) map.get("layout");
        if (StrUtil.isBlank(type)) {
            return R.error("布局类型为空");
        }
        if (StrUtil.isBlank(layout)) {
            return R.error("布局json数据为空");
        }
        if (LayoutTypeEnum.add.name().equals(type)) {
            old.setAddLayout(layout);
        } else if (LayoutTypeEnum.update.name().equals(type)) {
            old.setUpdateLayout(layout);
        } else if (LayoutTypeEnum.batch.name().equals(type)) {
            old.setBatchLayout(layout);
        }
        formDao.updateById(old);
        return R.success();
    }

    @GetMapping("/form/findAddFormInfoById")
    public R findAddFormInfoById(Long formId) {
        DiyForm obj = formDao.getById(formId);
        A.isInDatabase(obj, "表单");
        return R.success(generateAddFormInfo(obj));
    }

    public Map<String, Object> generateAddFormInfo(DiyForm obj) {
        //获取公式
        List<DiyFormula> formulaList = formulaDao.list(new QueryWrapper<DiyFormula>()
                .lambda().eq(DiyFormula::getFormId, obj.getId()));
        //获取全部字段
        List<DiyColumn> columnList = columnDao.list(new QueryWrapper<DiyColumn>()
                .lambda().eq(DiyColumn::getFormId, obj.getId()));
        //获取全部字段ID
        List<Long> columnIdList = columnList.stream()
                .map(t -> t.getId())
                .collect(Collectors.toList());
        //获取新增布局
        List<DiyLayout> layoutList = layoutDao.list(new QueryWrapper<DiyLayout>()
                .lambda()
                .in(DiyLayout::getColumnId, columnIdList)
                .eq(DiyLayout::getType, LayoutTypeEnum.add.name()));
        //获取新增布局字段
        List<ObjectNode> addColumnList = layoutList.stream()
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", obj.getId());
        map.put("name", obj.getName());
        map.put("code", obj.getCode());
        map.put("formulas", formulaList);
        map.put("addColumns", addColumnList);
        return map;
    }

    @GetMapping("/form/findUpdateFormInfoById")
    public R findUpdateFormInfoById(Long formId) {
        DiyForm obj = formDao.getById(formId);
        A.isInDatabase(obj, "表单");
        return R.success(generateUpdateFormInfo(obj));
    }

    public Map<String, Object> generateUpdateFormInfo(DiyForm obj) {
        //获取公式
        List<DiyFormula> formulaList = formulaDao.list(new QueryWrapper<DiyFormula>()
                .lambda().eq(DiyFormula::getFormId, obj.getId()));
        //获取全部字段
        List<DiyColumn> columnList = columnDao.list(new QueryWrapper<DiyColumn>()
                .lambda().eq(DiyColumn::getFormId, obj.getId()));
        //获取全部字段ID
        List<Long> columnIdList = columnList.stream()
                .map(t -> t.getId())
                .collect(Collectors.toList());
        //获取更新布局
        List<DiyLayout> layoutList = layoutDao.list(new QueryWrapper<DiyLayout>()
                .lambda()
                .in(DiyLayout::getColumnId, columnIdList)
                .eq(DiyLayout::getType, LayoutTypeEnum.update.name()));
        //获取更新布局字段
        List<ObjectNode> updateColumnList = layoutList.stream()
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", obj.getId());
        map.put("name", obj.getName());
        map.put("code", obj.getCode());
        map.put("formulas", formulaList);
        map.put("updateColumns", updateColumnList);
        return map;
    }

    @GetMapping("/form/findBatchFormInfoById")
    public R findBatchFormInfoById(Long formId) {
        DiyForm obj = formDao.getById(formId);
        A.isInDatabase(obj, "表单");
        return R.success(generateBatchFormInfo(obj));
    }

    public Map<String, Object> generateBatchFormInfo(DiyForm obj) {
        //获取公式
        List<DiyFormula> formulaList = formulaDao.list(new QueryWrapper<DiyFormula>()
                .lambda().eq(DiyFormula::getFormId, obj.getId()));
        //获取全部字段
        List<DiyColumn> columnList = columnDao.list(new QueryWrapper<DiyColumn>()
                .lambda().eq(DiyColumn::getFormId, obj.getId()));
        //获取全部字段ID
        List<Long> columnIdList = columnList.stream()
                .map(t -> t.getId())
                .collect(Collectors.toList());
        //获取更新布局
        List<DiyLayout> layoutList = layoutDao.list(new QueryWrapper<DiyLayout>()
                .lambda()
                .in(DiyLayout::getColumnId, columnIdList)
                .eq(DiyLayout::getType, LayoutTypeEnum.batch.name()));
        //获取批量更新布局字段
        List<ObjectNode> batchColumnList = layoutList.stream()
                .map(t -> generateColumnOn(t, columnList))
                .collect(Collectors.toList());

        Map<String, Object> map = new LinkedHashMap<>();
        map.put("id", obj.getId());
        map.put("name", obj.getName());
        map.put("code", obj.getCode());
        map.put("formulas", formulaList);
        map.put("batchColumns", batchColumnList);
        return map;
    }

    @GetMapping("/form/column")
    public R getFormColumn(Long formId) {
        List<DiyColumn> columnList = columnDao.list(new QueryWrapper<DiyColumn>()
                .lambda().eq(DiyColumn::getFormId, formId));
        return R.success(columnList);
    }

    @PostMapping("/form/pageTable")
    @ApiOperation("查询所有数据库表")
    public R pageTable(@RequestBody FormTablePageReq req) {
        Page<Map<String, Object>> list= formService.pageTable(req);
        return R.success(list);
    }

    @GetMapping("/form/importTable")
    @ApiOperation("导入表结构到动态表单系统")
    public R importTable(@RequestParam("tableName") String tableName) {
        formService.importFromTable(tableName);
        return R.success();
    }

    @GetMapping("/form/getByCode")
    public R getByCode(@Param("code") String  code) {
        DiyForm obj = formService.getByCode(code);
        return R.success(obj);
    }

}
